//void move(int axis, int plus);
//int is_limit_switch_pressed(void);
//int is_hold_button_pressed(void);
//int get_z_switches_type(void); returns 0 - for none, 1 for z_top and z_near
//int is_z_top(void);
//int is_z_near(void);
//void init_output(void);
//void drill_onoff(int onoff);
//void welcome_notice();
//void goodbye_notice();
//void on_every_cycle();

char variables_filename[256]="stored.ini";


void get_value(char *command, int *bufpos, double *value)
{
	char number[30];
	int numpos=0;
	while(command[*bufpos]=='+' || command[*bufpos]=='-'
		|| command[*bufpos]=='1' || command[*bufpos]=='2'
		|| command[*bufpos]=='3' || command[*bufpos]=='4'
		|| command[*bufpos]=='5' || command[*bufpos]=='6'
		|| command[*bufpos]=='7' || command[*bufpos]=='8'
		|| command[*bufpos]=='9' || command[*bufpos]=='0'
		|| command[*bufpos]=='.'){
		number[numpos] = command[*bufpos];
		numpos++;
		(*bufpos) = (*bufpos)+1;
	}
	number[numpos] = 0;
	sscanf(number, "%lf", value);
}

void load_things(void)
{
	FILE *stream;
	
	/* open the file to read */
	if((stream  = fopen( variables_filename, "rb" )) == NULL)
	{
		return;
	}
	
	fread(&globals, sizeof(global_variables), 1, stream);
	fclose(stream);
}

void save_things(void)
{
	FILE *stream;
	
	/* open the file to read */
	if((stream  = fopen( variables_filename, "wb" )) == NULL)
	{
		return;
	}
	
	fwrite(&globals, sizeof(global_variables), 1, stream);
	fclose(stream);
}

void dan_delay(double delay_time)
{long count;
for(count=0;count<delay_time;count++);
}

void wait_for_seconds(double seconds){
	long count;
	long end = seconds * 400000;
	for(count = 0; count<end; count++){
		double x = 3;
		double y = x*x;
	}
}

void print_where_we_are(int doing_tap_file, int waiting_for_continue, int move_type){
	if(doing_tap_file && !waiting_for_continue){
		int y = wherey();
		gotoxy(1,y-1);  
		delline();
	}
	// print current position
	int temp_x = (int)GetXWanted(globals.x_value) + (int)globals.x_offset;
	int temp_y = (int)GetYWanted(globals.y_value) + (int)globals.y_offset;
	int temp_z = (int)globals.z_value + (int)globals.tools[globals.current_tool];
	char message[1024];
	sprintf(message, "Current position, measured in steps is X%d Y%d Z%d", temp_x, temp_y, temp_z);
	//	if(!doing_tap_file || waiting_for_continue)cout<<message<<"\n";
	if(global_units){
		float ftemp_x = (GetXWanted(globals.x_value) + globals.x_offset) / global_x_steps_per_unit;
		float ftemp_y = (GetYWanted(globals.y_value) + globals.y_offset) / global_y_steps_per_unit;
		float ftemp_z = (globals.z_value + globals.tools[globals.current_tool]) / global_z_steps_per_unit;
		sprintf(message, "Current position, measured in units is X%f Y%f Z%f", ftemp_x, ftemp_y, ftemp_z);
	}
	else{
		float ftemp_x = GetXWanted(globals.x_value);
		float ftemp_y = GetYWanted(globals.y_value);
		float ftemp_z = globals.z_value + globals.tools[globals.current_tool];
		sprintf(message, "Position ( steps ) is X%f Y%f Z%f, limit pressed = %d", ftemp_x, ftemp_y, ftemp_z, is_limit_switch_pressed());
	}
	if(waiting_for_continue)cout<<"Type \"cont\" to continue processing tap file\n";
	cout<<message<<"\n";
	if(!doing_tap_file || waiting_for_continue){
		cout<<"TOOL "<<globals.current_tool+1<<", ";
		if(move_type == 0)cout<<"Move relative: ";
		else if(move_type == 1)cout<<"Rapid absolute: ";
		else if(move_type == 2)cout<<"Feed absolute, feed rate "<<(int)globals.feed_value<<", factor "<<global_feed_factor<<" : ";
	}
}

class FeedMove{
public:
	double x;
	double y;
	double z;
	double start_speed;
	double end_speed;
	double dist;
	double half_way;
	double max_speed;

	// constructor
	FeedMove(){
		x = 0;
		y = 0;
		z = 0;
		start_speed = 0;
		end_speed = 0;
		dist = 0;
		half_way = 0;
		max_speed = 0;
	}

	void SetHalfway(double acceleration){
		// calculate halfway position ( distance to change from positive acceleration to deceleration )
		double k = (start_speed - end_speed) / acceleration;
		double m = start_speed + end_speed;
		double a = 2 * acceleration;
		double b = 2 * start_speed + m + k * acceleration;
		double c = m * k - 2 * dist;
		double b2m4ac = b * b - 4 * a * c;
		if(b2m4ac<0){
			half_way = 0;
			return;
		}
		if(fabs(2*a)<0.0000000001){
			half_way = 0;
		}
		double t1 = (-b + sqrt(b2m4ac))/(2*a);
		double t2 = (-b - sqrt(b2m4ac))/(2*a);
		double t = t1;
		if(t1<0 || (t2>=0 && t2 < t1))t = t2;

		double vm = start_speed + t * acceleration;

		half_way = ( start_speed + vm )/2 * t;
	}

	void LimitSpeedsToMax(){
		if(start_speed>max_speed)start_speed = max_speed;
		if(end_speed>max_speed)end_speed = max_speed;
	}
};

int move_straight1(FeedMove* feed_move, double acceleration, int ignore_hold_and_limit);

int move_straight(double x_position_difference, double y_position_difference, double z_position_difference, double min_speed, double max_speed, double acceleration, int ignore_hold_and_limit)
{
	if(min_speed>max_speed)min_speed = max_speed;

	x_position_difference = (double)(long)x_position_difference;
	y_position_difference = (double)(long)y_position_difference;
	z_position_difference = (double)(long)z_position_difference;

	double dist = sqrt(x_position_difference * x_position_difference + y_position_difference * y_position_difference + z_position_difference * z_position_difference);
	if(dist<0.9)return 0;

	FeedMove feed_move;
	feed_move.x = x_position_difference;
	feed_move.y = y_position_difference;
	feed_move.z = z_position_difference;
	feed_move.start_speed = min_speed;
	feed_move.end_speed = min_speed;
	feed_move.dist = dist;
	feed_move.half_way = dist/2;
	feed_move.max_speed = max_speed;

	int temp_mouse;
	Mouse_Off(&temp_mouse, &temp_mouse, &temp_mouse);
	// disable the interrupts
	outp(0x21,global_interrupt_21);
	outp(0xA1,global_interrupt_A1);

	int problem_with_move = move_straight1(&feed_move, acceleration, ignore_hold_and_limit);

	// enable the interrupts
	outp(0xA1,0x00);
	outp(0x21,0x00);
	Mouse_On(&temp_mouse, &temp_mouse, &temp_mouse);//unmask all interrupts

	return problem_with_move;
}

#define IGNORE_CYCLES 50

long hold_cycles = 0;
long limit_cycles = 0;

int move_straight1(FeedMove* feed_move, double acceleration, int ignore_hold_and_limit)
{
	double x, y, z, speed;
	double position;
	double x_ratio, y_ratio, z_ratio;
	long x_step_pos, y_step_pos, z_step_pos;
	int x_plus, y_plus, z_plus;
	double temp_acceleration = acceleration;
	double neg_acceleration = -acceleration;

	if(feed_move->dist<0.9)return 0;
	position = 0;
	x_step_pos = 0;
	y_step_pos = 0;
	z_step_pos = 0;

	if(feed_move->x>0)x_plus = 1;
	else x_plus = 0;
	if(feed_move->y>0)y_plus = 1;
	else y_plus = 0;
	if(feed_move->z>0)z_plus = 1;
	else z_plus = 0;

	x_ratio = feed_move->x/feed_move->dist;
	y_ratio = feed_move->y/feed_move->dist;
	z_ratio = feed_move->z/feed_move->dist;

	speed = feed_move->start_speed;

	int was_hold = 0;

	while(1)
	{
		double temp_speed;

		on_every_cycle();

		int z_near = is_z_near();
		int z_top = is_z_top();
		int limit = is_limit_switch_pressed() && !(ignore_hold_and_limit);
		int hold = is_hold_button_pressed() && !(ignore_hold_and_limit);

		if(limit){
			if(limit_cycles<IGNORE_CYCLES){
				limit_cycles++;
				limit = 0;
			}
		}
		else{
			limit_cycles = 0;
		}

		if(hold){
			if(hold_cycles<IGNORE_CYCLES){
				hold_cycles++;
				hold = 0;
			}
		}
		else{
			hold_cycles = 0;
		}

		if(limit){
			return 3;
		}

		temp_speed = speed;
		if(speed>feed_move->max_speed)temp_speed = feed_move->max_speed;
		else{
			if(temp_speed<global_min_speed){
				if(was_hold){
					globals.x_value += x_step_pos;
					globals.y_value += y_step_pos;
					globals.z_value += z_step_pos;
					print_where_we_are(0,0,0);
					cout<<"!!!!! HOLD BUTTON PRESSED !!!!! Do you want to continue? ( press \"y\" or \"n\" )\n";
					int ch = getch();
					if(ch == 'y' || ch == 'Y'){
						return move_straight(feed_move->x - x, feed_move->y - y, feed_move->z - z, global_min_speed, feed_move->max_speed, acceleration, ignore_hold_and_limit);
					}
					else{
						// finish;
						return 1;
					}
				}
				temp_speed = global_min_speed;
			}
		}

		position += temp_speed;

		if((z_near && z_ratio>0.001) || limit || hold){
			temp_acceleration = neg_acceleration;
			speed = temp_speed;
			was_hold = hold;
		}
		
		if(position>feed_move->half_way)temp_acceleration = neg_acceleration;
		else temp_acceleration = temp_acceleration;
		
		speed += temp_acceleration;
		x = position * x_ratio;
		y = position * y_ratio;
		z = position * z_ratio;

		if(position > feed_move->dist)break;
		
		if(z_plus && z > z_step_pos || !z_plus && z < z_step_pos)
		{
			if( z_top && z_plus){
				cout<<"Z TOP REACHED\n";
				globals.x_value += x_step_pos;
				globals.y_value += y_step_pos;
				globals.z_value += z_step_pos;
				// finish;
				return 2;
			}
			else{
				move(2, z_plus);
				z_step_pos += (-1 + 2 * z_plus);
				if(z_plus && z > z_step_pos || !z_plus && z < z_step_pos)
				{
					printf(" speed bigger than one step ( z move_striaght )\n");
				}
			}
		}
		
		if(x_plus && x > x_step_pos || !x_plus && x < x_step_pos)
		{
			move(0, x_plus);
			x_step_pos += (-1 + 2 * x_plus);
			if(x_plus && x > x_step_pos || !x_plus && x < x_step_pos)
			{
				printf(" speed bigger than one step ( x move_straight )\n");
			}
		}
		
		if(y_plus && y > y_step_pos || !y_plus && y < y_step_pos)
		{
			move(1, y_plus);
			y_step_pos += (-1 + 2 * y_plus);
			if(y_plus && y > y_step_pos || !y_plus && y < y_step_pos)
			{
				printf(" speed bigger than one step ( y move_straight )\n");
			}
		}
	}
	
	globals.x_value += x_step_pos;
	globals.y_value += y_step_pos;
	globals.z_value += z_step_pos;

	return 0; // for OK
}

void normalize_d (double v[3])
{
	double d=sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
	if(d==0.0)
	{
		//cerror_str(1, "zero length vector");
		v[0]=0;
		v[1]=0;
		v[2]=1;
		return;
	}
	v[0]/=d;
	v[1]/=d;
	v[2]/=d;
}

double distance_between_points(double *p1, double *p2)
{
	// 2 dimensional only
	return sqrt((p2[0]-p1[0])*(p2[0]-p1[0]) + (p2[1]-p1[1])*(p2[1]-p1[1]));
}

double bring_angle_beyond_angle(double angle, double start_angle)
{
	/*Given angle and start angle, this moves angle, by adding and/or  */
	/*subtracting 2 pi to angle, and brings it within the 2 pi radians */
	/*beyond the start angle.  This returns that final angle.    */
	while(angle>start_angle)angle-=2*global_users_pi;
	while(angle<start_angle)angle+=2*global_users_pi;
	return angle;
}

double angle_between_angles(double angle1, double angle2)
{
	/*This returns the angle anti-clockwise from angle 1 to angle 2*/
	double angle;
	
	angle2=bring_angle_beyond_angle(angle2, angle1);
	angle=angle2-angle1;
	if(angle>global_users_pi)angle=angle-2*global_users_pi;

	return angle;
}

double find_ang(double *p, double *c)
/* given position and center point returns an angle anti-clockwise
from x-axis in radians  */
/* so for a line use c as start point and p as finish */
{
	double x,y,angle;
	x=p[0]-c[0];
	y=p[1]-c[1];
	
	if(FEQZ(x))
	{
		if(FGTZ(y))
		{
			angle=global_users_pi/2;
		}
		else
		{
			angle=-global_users_pi/2;
		}
	}
	else
	{
		if(FGTZ(x))
		{
			angle=atan(y/x);
		}
		else
		{
			angle=global_users_pi+atan(y/x);
		}
	}
	if(FGT(angle,global_users_pi))
	{
		angle+=-2*global_users_pi;
	}

	return(angle);
}

int arc_contains_angle(double start_angle, double end_angle, int dir, double angle){
	int result = 0;
	if(dir == 1){
		while(angle - start_angle<-0.001)angle+=2*global_users_pi;
		while(end_angle - start_angle<-0.001)end_angle+=2*global_users_pi;
		if(angle-start_angle>0.001 && angle - end_angle<-0.001)result = 1;
	}
	else{
		while(angle - start_angle>0.001)angle-=2*global_users_pi;
		while(end_angle - start_angle>0.001)end_angle-=2*global_users_pi;
		if(angle-start_angle<-0.001 && angle - end_angle>0.001)result = 1;
	}
	return result;
}

int move_arc(double next_x_value, double next_y_value, double min_speed, double max_speed, double acceleration, int arc_direction, int dont_split)
{
	if(backlash_graphs_used()){
		cout<<"can't do arcs with variable backlash, please adjust your NC code generator to only do lines!\n";
		return 1;// problem with move
	}

	// cut an arc from the present position ( x_value, y_value )
	// to ( next_x_value, next_y_value ) around ( i_value, j_value )
	// arc_direction = 1 for CCW, -1 for CW
	
	double x, y, speed;
	double x_previous, y_previous;
	double dist, half_way, position;
	double x_ratio, y_ratio;
	long x_step_pos, y_step_pos;
	int x_plus, y_plus, temp_mouse;
	double x_position_difference, y_position_difference;
	double start[3], end[3], centre[3];
	double start_radius, end_radius, start_angle, end_angle;
	double total_angle, average_radius;
	double angle, radius;
	int first_pass = 1;
	double temp_acceleration = acceleration;
	double neg_acceleration = -acceleration;

	if(min_speed>max_speed)min_speed = max_speed;
	
	start[0] = 0;
	start[1] = 0;
	start[2] = 0;
	
	end[0] = next_x_value - globals.x_value;
	end[1] = next_y_value - globals.y_value;
	end[2] = 0;
	
	//find the centre point
	// if radius is positive then angle is less than 180

	
	double  half_start_end = distance_between_points(start, end)/2;
	double temp_radius = sqrt(globals.i_value * globals.i_value +globals.j_value * globals.j_value);

	if(half_start_end>fabs(temp_radius)){
		temp_radius = half_start_end;
	}
	double  dist_from_chord = sqrt((temp_radius * temp_radius) - (half_start_end * half_start_end));
	double  v1[3] = {end[0] - start[0], end[1] - start[1], 0};
	double  v2[3];
	
	// find unit vector along chord
	normalize_d(v1);
	
	// find unit vector towards centre point
	if((arc_direction == 1) == (temp_radius>0)){
		// rotate vector 90 degrees left
		v2[0] = -v1[1];
		v2[1] = v1[0];
	}
	else{
		// rotate vector 90 degrees right
		v2[0] = v1[1];
		v2[1] = -v1[0];
	}
	
	v2[2] = 0;
	
	centre[0] = (start[0] + end[0])/2 + dist_from_chord * v2[0];
	centre[1] = (start[1] + end[1])/2 + dist_from_chord * v2[1];
	centre[2] = 0;
	
	start_radius = distance_between_points(start, centre);
	end_radius = distance_between_points(end, centre);

	start_angle = find_ang(start, centre);
	end_angle = find_ang(end, centre);
	
	if(!dont_split){
		double first_quadrant_angle;
		if(arc_direction == 1){
			first_quadrant_angle = -8 * global_users_pi ;
			while(start_angle- first_quadrant_angle>-0.001)first_quadrant_angle+=global_users_pi/2;
		}
		else{
			first_quadrant_angle = 8 * global_users_pi;
			while(start_angle- first_quadrant_angle<0.001)first_quadrant_angle-=global_users_pi/2;
		}
		if(arc_contains_angle(start_angle, end_angle, arc_direction, first_quadrant_angle)){
			radius = start_radius + ((first_quadrant_angle - start_angle)/(end_angle - start_angle)) * (end_radius - start_radius);
			double next_next_x = globals.x_value + centre[0] + radius * cos(first_quadrant_angle);
			double next_next_y = globals.y_value + centre[1] + radius * sin(first_quadrant_angle);
			int problem_with_move = move_arc(next_next_x, next_next_y, min_speed, max_speed, acceleration, arc_direction, 1) ;
			// delay a bit
			wait_for_seconds(0.01);

			if(!problem_with_move)problem_with_move = move_arc(next_x_value, next_y_value, min_speed, max_speed, acceleration, arc_direction, 1) ;
			return problem_with_move;
		}
	}
	
	total_angle = angle_between_angles(start_angle, end_angle);
	if(total_angle<0)total_angle+=2*global_users_pi;
	if(arc_direction == -1)total_angle = 2*global_users_pi - total_angle;
	
	average_radius = ( start_radius + end_radius ) / 2;
	
	dist = average_radius * total_angle;
	
	if(dist<0.9){
		return 0;
	}
	angle = start_angle;
	position = 0;
	x_step_pos = 0;
	y_step_pos = 0;
	half_way = dist/2;
	
	double temp_angle = start_angle;
	while(temp_angle>2*global_users_pi - 0.001)temp_angle-=2*global_users_pi;
	while(temp_angle<-0.001)temp_angle+=2*global_users_pi;
	if(arc_direction == 1){
		if(temp_angle > global_users_pi - 0.001)x_plus = 1;
		else x_plus = 0;
		double tttt = (((double)3/2)*(global_users_pi)) - 0.001;
		double tttt2 = (((double)1/2) * (global_users_pi)) - 0.001;
		if(temp_angle >tttt)y_plus = 1;
		else if(temp_angle > tttt2)y_plus = 0;
		else y_plus = 1;
	}
	else{
		if(temp_angle > global_users_pi + 0.001)x_plus = 0;
		else if(temp_angle >0.001)x_plus = 1;
		else x_plus = 0;
		double tttt = (((double)3/2)*(global_users_pi)) + 0.001;
		double tttt2 =(((double)1/2)*(global_users_pi)) + 0.001;
		if(temp_angle >tttt)y_plus = 0;
		else if(temp_angle > tttt2)y_plus = 1;
		else y_plus = 0;
	}

	if(x_plus && globals.xdir == -1 || !x_plus && globals.xdir == 1 || y_plus && globals.ydir == -1 || !y_plus && globals.ydir == 1){
		int old_xdir, old_ydir;
		double backlash_x = 0, backlash_y =0;

		if(x_plus && globals.xdir == -1 || !x_plus && globals.xdir == 1){
			old_xdir = globals.xdir;
			if(x_plus)globals.xdir = 1;
			else globals.xdir = -1;
			if(globals.xdir == -old_xdir)backlash_x = globals.xdir * global_x_backlash_steps;
			if(backlash_x){
				move_straight(backlash_x, 0, 0, global_min_speed, global_max_speed, global_acceleration, 0);
				globals.x_value -= backlash_x;
			}
		}
		if(y_plus && globals.ydir == -1 || !y_plus && globals.ydir == 1){
			old_ydir = globals.ydir;
			if(y_plus)globals.ydir = 1;
			else globals.ydir = -1;
			if(globals.ydir == -old_ydir)backlash_y = globals.ydir * global_y_backlash_steps;
			if(backlash_y){
				move_straight(0, backlash_y, 0, global_min_speed, global_max_speed, global_acceleration, 0);
				globals.y_value -= backlash_y;
			}
		}
	}
	speed = min_speed;

	Mouse_Off(&temp_mouse, &temp_mouse, &temp_mouse);
	outp(0x21,global_interrupt_21);
	outp(0xA1,global_interrupt_A1);

	int was_hold = 0;

	while(1)
	{
		on_every_cycle();
		
		double temp_speed;
		
		int limit = is_limit_switch_pressed();
		int hold = is_hold_button_pressed();
		
		if(limit){
			if(limit_cycles<IGNORE_CYCLES){
				limit_cycles++;
				limit = 0;
			}
		}
		else{
			limit_cycles = 0;
		}

		if(hold){
			if(hold_cycles<IGNORE_CYCLES){
				hold_cycles++;
				hold = 0;
			}
		}
		else{
			hold_cycles = 0;
		}

		if(limit){
			cout<<"!!!!! LIMIT HIT !!!!! type \"MOVE X-1\" or something to move off limit\n";
			cout<<"YOUR CO-ORDINATES WILL NOW BE WRONG ON ANY AXES THAT WERE MOVING\n";
			// finish;
			return 3;
		}
		
		temp_speed = speed;
		if(speed>max_speed)temp_speed = max_speed;
		else{
			if(temp_speed<min_speed){
				if(was_hold){
					globals.x_value += x_step_pos;
					globals.y_value += y_step_pos;
					print_where_we_are(0,0,0);
					cout<<"!!!!! HOLD BUTTON PRESSED !!!!! Do you want to continue? ( press \"Y\" or \"N\" )\n";
					int ch = getch();
					if(ch == 'y' || ch == 'Y'){
						return move_arc(next_x_value, next_y_value, min_speed, max_speed, acceleration, arc_direction, dont_split);
					}
					else{
						// finish;
						return 1;
					}
				}
				temp_speed = min_speed;
			}
		}
		
		position += temp_speed;
		
		if(limit || hold){
			temp_acceleration = neg_acceleration;
			speed = temp_speed;
			was_hold = hold;
		}
		
		if(position>half_way)temp_acceleration = neg_acceleration;
		else temp_acceleration = temp_acceleration;
		
		speed += temp_acceleration;
		
		if(arc_direction>0){
			angle = start_angle + (position/dist) * total_angle;
		}
		else{
			angle = start_angle - (position/dist) * total_angle;
		}
		radius = start_radius + (position/dist) * (end_radius - start_radius);
		
		x = centre[0] + radius * cos(angle);
		y = centre[1] + radius * sin(angle);
		
		if(!first_pass){
			if(x>x_previous)x_plus = 1;
			else x_plus = 0;
			if(y>y_previous)y_plus = 1;
			else y_plus = 0;
		}
		
		first_pass = 0;
		x_previous = x;
		y_previous = y;
		
		if(position > dist)break;
		
		if(x_plus && x > x_step_pos || !x_plus && x < x_step_pos)
		{
			move(0, x_plus);
			x_step_pos += (-1 + 2 * x_plus);
			if(x_plus && x > x_step_pos || !x_plus && x < x_step_pos)
			{
				printf(" speed bigger than one step ( x move_arc )\n");
			}
		}
		
		if(y_plus && y > y_step_pos || !y_plus && y < y_step_pos)
		{
			move(1, y_plus);
			y_step_pos += (-1 + 2 * y_plus);
			if(y_plus && y > y_step_pos || !y_plus && y < y_step_pos)
			{
				printf(" speed bigger than one step ( y move_arc )\n");
				cout<<"y_plus "<<y_plus<<" y "<<y<<" y_step_pos "<<y_step_pos<<"\n";
				getch();
			}
		}
		
	}
	
	outp(0x21,0);
	outp(0xA1,0);
	Mouse_On(&temp_mouse, &temp_mouse, &temp_mouse);//unmask all interrupts
	
	globals.x_value += x_step_pos;
	globals.y_value += y_step_pos;
	
	return 0;// no problem
}

void read_first_word(FILE *fp, char *command, char *string, int *bufpos, int mode, int stop_at_spaces)
{
	//given a file( if necessary ) and a string and a position
	// mode = 1  means  from a file using the string
	// mode = 2  means  just from the string
	// mode = 3  means  from keyboard
	char l;

	while(1)
	{
		if(mode == 1)fscanf (fp, "%c", &(command[*bufpos]));
		else if(mode == 2)sscanf(string, "%c", &(command[*bufpos]));
		else if(mode == 3)scanf("%c", &(command[*bufpos]));
		command[*bufpos] = toupper(command[*bufpos]);

		*bufpos = *bufpos + 1;
		l=command[(*bufpos) - 1];
		if(l=='{' || l=='[' || l=='}' || l==']' || l==0 || l=='\n' || l=='\r')break;
		if(stop_at_spaces && (l == ' ' || l == '\t'))break;

		if(*bufpos>511)break;
	}
	command[*bufpos-1]=0;
}

int is_number(char letter)
{
	if(letter == '0')return 1;
	if(letter == '.')return 1;
	if(letter == '-')return 1;
	if(letter == '1')return 1;
	if(letter == '2')return 1;
	if(letter == '3')return 1;
	if(letter == '4')return 1;
	if(letter == '5')return 1;
	if(letter == '6')return 1;
	if(letter == '7')return 1;
	if(letter == '8')return 1;
	if(letter == '9')return 1;
	
	return 0;
}

void look_for_meaning(char *command, int *bufpos, int *move_found,
					  int *g0_found, int *g1_found, int *g2_found, int *g3_found, int *g4_found, double *next_x_value, double *next_y_value,
					  double *next_z_value, double *next_i_value, double *next_j_value, int *exit_found, int *x_found, int *y_found,
					  int *z_found, int *i_found, int *j_found, int *tap_found, char* tap_file, int *tr_found, int *m2_found, int *feed_found,
					  double *next_feed_value, int *set_found, int *wait_found, int *d_found, double *next_d_value, int *mm_found, int *steps_found, int *help_found,
					  int *tool_found, int *tool_value, int *m6_found, int *cont_found, int *dwell_found, double *next_dwell_value,
					  int *zerox_found, int *zeroy_found)
{
	// look for "move"
	if(command[*bufpos] == 'M'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == 'O'){
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'V'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'E'){
					*bufpos = *bufpos + 1;
					*move_found = 1;
				}
			}
		}
		// look for m2 or m02
		else if(command[*bufpos] == '0'){
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == '2'){
				*bufpos = *bufpos + 1;
				if(!is_number(command[*bufpos]))
					*m2_found = 1;
			}
			else if(command[*bufpos] == '6'){
				*bufpos = *bufpos + 1;
				if(!is_number(command[*bufpos]))
					*m6_found = 1;
			}
		}
		else if(command[*bufpos] == '2'){
			*bufpos = *bufpos + 1;
			if(!is_number(command[*bufpos]))
				*m2_found = 1;
		}
		else if(command[*bufpos] == '6'){
			*bufpos = *bufpos + 1;
			if(!is_number(command[*bufpos]))
				*m6_found = 1;
		}
		// look for mm ( use millimetres as units )
		else if(command[*bufpos] == 'M'){
			*bufpos = *bufpos + 1;
			*mm_found = 1;
		}
	}
	
	//look for "g0", "g01", "g02", "g03", "g1", "go"
	else if(command[*bufpos] == 'G'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == '0'){
			*bufpos = *bufpos + 1;
			if(!is_number(command[*bufpos]))
				*g0_found = 1;
			else if(command[*bufpos] == '0'){
				*bufpos = *bufpos + 1;
				if(!is_number(command[*bufpos]))
					*g0_found = 1;
			}
			else if(command[*bufpos] == '1'){
				*bufpos = *bufpos + 1;
				if(!is_number(command[*bufpos]))
					*g1_found = 1;
			}
			else if(command[*bufpos] == '2'){
				*bufpos = *bufpos + 1;
				if(!is_number(command[*bufpos]))
					*g2_found = 1;
			}
			else if(command[*bufpos] == '3'){
				*bufpos = *bufpos + 1;
				if(!is_number(command[*bufpos]))
					*g3_found = 1;
			}
			else if(command[*bufpos] == '4'){
				*bufpos = *bufpos + 1;
				if(!is_number(command[*bufpos]))
					*g4_found = 1;
			}
		}
		else if(command[*bufpos] == '1'){
			*bufpos = *bufpos + 1;
			if(!is_number(command[*bufpos]))
				*g1_found = 1;
		}
		else if(command[*bufpos] == 'O'){
			*bufpos = *bufpos + 1;
			if(!is_number(command[*bufpos]))
				*g0_found = 1;
		}
		else if(command[*bufpos] == '2'){
			*bufpos = *bufpos + 1;
			if(!is_number(command[*bufpos]))
				*g2_found = 1;
		}
		else if(command[*bufpos] == '3'){
			*bufpos = *bufpos + 1;
			if(!is_number(command[*bufpos]))
				*g3_found = 1;
		}
		else if(command[*bufpos] == '4'){
			*bufpos = *bufpos + 1;
			if(!is_number(command[*bufpos]))
				*g4_found = 1;
		}
	}

	// look for "help"
	else if(command[*bufpos] == 'H'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == 'E'){
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'L'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'P'){
					*bufpos = *bufpos + 1;
					*help_found = 1;
				}
			}
		}
	}

	//look for "XValue", "X Value"
	else if(command[*bufpos] == 'X'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == ' ')
			*bufpos = *bufpos + 1;
		if(is_number(command[*bufpos]))
		{
			get_value(command, bufpos, next_x_value);
			if(global_units == 1)*next_x_value *= global_x_steps_per_unit;
			*x_found = 1;
		}
		else *bufpos = *bufpos + 1;
	}

	//look for "YValue", "Y Value"
	else if(command[*bufpos] == 'Y'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == ' ')
			*bufpos = *bufpos + 1;
		if(is_number(command[*bufpos]))
		{
			get_value(command, bufpos, next_y_value);
			if(global_units == 1)*next_y_value *= global_y_steps_per_unit;
			*y_found = 1;
		}
		else *bufpos = *bufpos + 1;
	}

	//look for "IValue", "I Value"
	else if(command[*bufpos] == 'I'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == ' ')
			*bufpos = *bufpos + 1;
		if(is_number(command[*bufpos]))
		{
			get_value(command, bufpos, next_i_value);
			if(global_units == 1)*next_i_value *= global_x_steps_per_unit;
			*i_found = 1;
		}
		else *bufpos = *bufpos + 1;
	}
	
	//look for "JValue", "J Value"
	else if(command[*bufpos] == 'J'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == ' ')
			*bufpos = *bufpos + 1;
		if(is_number(command[*bufpos]))
		{
			get_value(command, bufpos, next_j_value);
			if(global_units == 1)*next_j_value *= global_y_steps_per_unit;
			*j_found = 1;
		}
		else *bufpos = *bufpos + 1;
	}
	
	//look for "/Value", "/ Value"
	else if(command[*bufpos] == '/'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == ' ')
			*bufpos = *bufpos + 1;
		if(is_number(command[*bufpos]))
		{
			get_value(command, bufpos, next_dwell_value);
			*dwell_found = 1;
		}
		else *bufpos = *bufpos + 1;
	}

	//look for "FValue", "F Value"
	else if(command[*bufpos] == 'F'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == ' ')
			*bufpos = *bufpos + 1;
		else if(command[*bufpos] == 'A'){
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'C'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'T'){
					*bufpos = *bufpos + 1;
					if(command[*bufpos] == ' ')
						*bufpos = *bufpos + 1;
					if(is_number(command[*bufpos]))
					{
						get_value(command, bufpos, &global_feed_factor);
					}
					else *bufpos = *bufpos + 1;
				}
			}
			return;
		}
		if(is_number(command[*bufpos]))
		{
			get_value(command, bufpos, next_feed_value);
			*feed_found = 1;
		}
		else *bufpos = *bufpos + 1;
	}
	
	//look for "ZValue", "Z Value"
	// "Z HOME", "ZHOME", "ZERO"
	else if(command[*bufpos] == 'Z'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == ' ')
			*bufpos = *bufpos + 1;
		else if(command[*bufpos] == 'E'){
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'R'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'O'){
					*bufpos = *bufpos + 1;
					if(command[*bufpos] == 'X'){
						*bufpos = *bufpos + 1;
						*zerox_found = 1;
					}
					else if(command[*bufpos] == 'Y'){
						*bufpos = *bufpos + 1;
						*zeroy_found = 1;
					}
				}
			}
			return;
		}
		
		if(is_number(command[*bufpos]))
		{
			get_value(command, bufpos, next_z_value);
			if(global_units == 1)*next_z_value *= global_z_steps_per_unit;
			*next_z_value -= globals.tools[globals.current_tool];
			*z_found = 1;
		}
		else *bufpos = *bufpos + 1;
	}

	//look for "DValue", "D Value"
	else if(command[*bufpos] == 'D'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == ' ')
			*bufpos = *bufpos + 1;
		if(is_number(command[*bufpos]))
		{
			get_value(command, bufpos, next_d_value);
			*d_found = 1;
		}
		else *bufpos = *bufpos + 1;
	}

	// look for "exit"
	else if(command[*bufpos] == 'E'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == 'X'){
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'I'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'T'){
					*bufpos = *bufpos + 1;
					*exit_found = 1;
				}
			}
		}
	}
	
	// look for "wait"
	else if(command[*bufpos] == 'W'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == 'A'){
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'I'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'T'){
					*bufpos = *bufpos + 1;
					*wait_found = 1;
				}
			}
		}
	}

	// look for "tap"
	else if(command[*bufpos] == 'T'){
		*bufpos = *bufpos + 1;
		int get_tool = 0;
		int tc_found = 0;
		int t_space_or_tc_space_found = 0;
		if(command[*bufpos] == 'C'){
			*bufpos = *bufpos + 1;
			tc_found = 1;
		}
		if(command[*bufpos] == ' '){
			*bufpos = *bufpos + 1;
			t_space_or_tc_space_found = 1;
		}
		if(is_number(command[*bufpos]))get_tool = 1;
		
		if(!get_tool && !tc_found && !t_space_or_tc_space_found){
			if(command[*bufpos] == 'A'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'P'){
					*bufpos = *bufpos + 1;
					*tap_found = 1;
					while(command[*bufpos] == ' '){
						// remove spaces
						*bufpos = *bufpos + 1;
					}
					// get the rest of the line
					int temp = 0;
					while(command[*bufpos]){
						tap_file[temp] = command[*bufpos];
						*bufpos = *bufpos + 1;
						temp++;
					}
					tap_file[temp]=0;
				}
			}
			else if(command[*bufpos] == 'R'){
				*bufpos = *bufpos + 1;
				*tr_found = 1;
			}
			else if(command[*bufpos] == 'O'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'O'){
					*bufpos = *bufpos + 1;
					if(command[*bufpos] == 'L'){
						*bufpos = *bufpos + 1;
						if(command[*bufpos] == ' ')
							*bufpos = *bufpos + 1;
						if(is_number(command[*bufpos]))get_tool = 1;
					}
				}
			}
		}
		if(get_tool){
			double temp_double;
			get_value(command, bufpos, &temp_double);
			if(tc_found)*tool_found = 2;
			else *tool_found = 1;
			*tool_value = temp_double;
		}
		else *bufpos = *bufpos + 1;
	}
	
	// look for "rap"
	else if(command[*bufpos] == 'R'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == 'A'){
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'P'){
				*bufpos = *bufpos + 1;
				*g0_found = 1;
			}
		}
	}
	
	else if(command[*bufpos] == 'C'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == 'O'){
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'N'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'T'){
					*bufpos = *bufpos + 1;
					*cont_found = 1;
				}
			}
		}
	}
	
	// look for "set" or "steps"
	else if(command[*bufpos] == 'S'){
		*bufpos = *bufpos + 1;
		if(command[*bufpos] == 'T'){
			// look for "steps"
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'E'){
				*bufpos = *bufpos + 1;
				if(command[*bufpos] == 'P'){
					*bufpos = *bufpos + 1;
					if(command[*bufpos] == 'S'){
						*bufpos = *bufpos + 1;
						*steps_found = 1;
					}
				}
			}
		}
		else if(command[*bufpos] == 'E'){
			// look for "set"
			*bufpos = *bufpos + 1;
			if(command[*bufpos] == 'T'){
				*bufpos = *bufpos + 1;
				*set_found = 1;
			}
		}
	}

	else *bufpos = *bufpos + 1;
	
}

void help_notice(){
	cout<<"You start in MOVE mode, you can type \"X10\" to move 10mm to the right.\n";
	cout<<"Press the \"ENTER\" key to issue your command\n";
	cout<<"You can type:\n";
	cout<<" \"HELP\" to bring up these instructions again\n";
	cout<<" \"G1 X5 Y3 Z0\" for example to feed in absolute coordinates\n";
	cout<<" \"G0 X10 Y-10 Z4\" to rapid to a position in absolute coordinates\n";
	cout<<" \"MOVE X10 Y10 Z10 to rapid relative to the current position\n";
	cout<<" \"SET X10 Z0\" to set the current coordinates - no move takes place\n";
	cout<<" \"TAP a:\\heeks.tap\" to start machining an NC file\n";
	cout<<" \"STEPS\" to change instructions to move in steps of the motors\n";
	cout<<" \"MM\" to change back to mm\n";
	cout<<" \"F200\" to change the feedrate ( F100 means 100 percent of normal )\n";
	cout<<" \"P\" to repeat the previous command\n";
	cout<<" \"EXIT\" to save the current position and turn off the machine\n\n";
	cout<<"YOU MUST EXIT BEFORE TURNING OFF TO RETAIN THE CURRENT COORDINATES\n";
}

struct MoveListItem{
	FeedMove* feed_move;
	MoveListItem* prev_item;
	MoveListItem* next_item;
};

#if 0
class FeedMoveBuffer{
private:
	double prev_x_value;
	double prev_y_value;
	double prev_z_value;
	MoveListItem* first_item;
	MoveListItem* last_item;
	int num_moves;

public:
	FeedMoveBuffer(){
		first_item = NULL;
		last_item = NULL;
		num_moves = 0;
	}

	~FeedMoveBuffer(){
		// should be empty here ( first_item should be NULL )
	}

	// member functions
	void AddFeedMove(int x_found, double next_x_value, int y_found, double next_y_value, int z_found, double next_z_value){
		if(num_moves == 0){
			prev_x_value = globals.x_value;
			prev_y_value = globals.y_value;
			prev_z_value = globals.z_value;
		}

		// sort out backlash
		double backlash_x = 0, backlash_y =0;
		int old_xdir, old_ydir, old_zdir;

		if(x_found){
			old_xdir = globals.xdir;
			if(next_x_value - prev_x_value >= 0)globals.xdir = 1;
			else globals.xdir = -1;
			if(globals.xdir == -old_xdir)backlash_x = globals.xdir * global_x_backlash_steps;
		}

		if(y_found){
			old_ydir = globals.ydir;
			if(next_y_value - prev_y_value >= 0)globals.ydir = 1;
			else globals.ydir = -1;
			if(globals.ydir == -old_ydir)backlash_y = globals.ydir * global_y_backlash_steps;
		}

		if(backlash_x || backlash_y){
			DoAllFeedMovesAndClear();
			move_straight(backlash_x, backlash_y, 0, global_min_speed, global_max_speed, global_acceleration, 0);
			// restore globals after backlash move
			globals.x_value -= backlash_x;
			globals.y_value -= backlash_y;
		}

		// make a new list item
		MoveListItem* new_item = new MoveListItem;
		new_item->feed_move = new FeedMove;
		new_item->next_item = NULL;
		new_item->prev_item = NULL;
		new_item->feed_move->max_speed = global_feed_speed * (globals.feed_value/100);
		if(new_item->feed_move->max_speed > global_max_speed)new_item->feed_move->max_speed = global_max_speed;
		if(last_item){
			last_item->next_item = new_item;
			new_item->prev_item = last_item;
		}

		// set it's values
		new_item->feed_move->x = 0;
		new_item->feed_move->y = 0;
		new_item->feed_move->z = 0;
		if(x_found)new_item->feed_move->x = (double)(long)(next_x_value - prev_x_value);
		if(y_found)new_item->feed_move->y = (double)(long)(next_y_value - prev_y_value);
		if(z_found)new_item->feed_move->z = (double)(long)(next_z_value - prev_z_value);

		if(new_item->feed_move->z <-2000){
			// just to break at
			prev_x_value = next_x_value;
		}

		if(x_found)prev_x_value = next_x_value;
		if(y_found)prev_y_value = next_y_value;
		if(z_found)prev_z_value = next_z_value;
		new_item->feed_move->end_speed = new_item->feed_move->max_speed;
		new_item->feed_move->dist = sqrt(new_item->feed_move->x * new_item->feed_move->x + new_item->feed_move->y * new_item->feed_move->y + new_item->feed_move->z * new_item->feed_move->z);

		if(new_item->feed_move->dist < 0.0000000001){
			delete new_item->feed_move;
			delete new_item;
			if(last_item)last_item->next_item = NULL;
			return;
		}

		if(last_item){
			double x_diff = fabs(last_item->feed_move->x/last_item->feed_move->dist - new_item->feed_move->x/new_item->feed_move->dist);
			double y_diff = fabs(last_item->feed_move->y/last_item->feed_move->dist - new_item->feed_move->y/new_item->feed_move->dist);
			double z_diff = fabs(last_item->feed_move->z/last_item->feed_move->dist - new_item->feed_move->z/new_item->feed_move->dist);

			double max_diff = x_diff;
			if(y_diff>max_diff)max_diff = y_diff;
			if(z_diff>max_diff)max_diff = z_diff;

			if(max_diff >= 0.9999999999){
				if(last_item)last_item->next_item = NULL;
				DoAllFeedMovesAndClear();
				new_item->feed_move->start_speed = global_min_speed;
				new_item->prev_item=NULL;
				first_item = new_item;
				prev_x_value = globals.x_value;
				prev_y_value = globals.y_value;
				prev_z_value = globals.z_value;
			}
			else{
				double min_max_speed = last_item->feed_move->max_speed;
				if(new_item->feed_move->max_speed < min_max_speed)min_max_speed = new_item->feed_move->max_speed;

				double speed = 1000000000;
				if(fabs(max_diff)>0.000000001)speed = global_min_speed / max_diff;

				// calculate max possible speed
				double vp1 = sqrt(last_item->feed_move->start_speed * last_item->feed_move->start_speed + global_acceleration*2*last_item->feed_move->dist);
				double vp2 = -vp1;
				double t1 = 2*last_item->feed_move->dist / (last_item->feed_move->start_speed + vp1);
				double t2 = 2*last_item->feed_move->dist / (last_item->feed_move->start_speed + vp2);
				double vp = vp1;
				if(t1<0 || t2>=0 && t2<t1){
					vp = vp2;
				}

				if(speed > vp)speed = vp;

				last_item->feed_move->end_speed = speed;
				new_item->feed_move->start_speed = speed;

				last_item->feed_move->LimitSpeedsToMax();
			}
		}
		else{
			new_item->feed_move->start_speed = global_min_speed;
		}
		new_item->feed_move->LimitSpeedsToMax();

		// set the buffer's first and last item pointers
		if(first_item == NULL)first_item = new_item;
		last_item = new_item;
		num_moves++;
	}

	int DoAllFeedMovesAndClear();

	int size(){
		return num_moves;
	}

}feed_move_buffer;

int FeedMoveBuffer::DoAllFeedMovesAndClear(){
		int temp_mouse;
		Mouse_Off(&temp_mouse, &temp_mouse, &temp_mouse);
		// disable the interrupts
		outp(0x21,global_interrupt_21);
		outp(0xA1,global_interrupt_A1);

		// go back from the end reducing speeds
		if(last_item){
			last_item->feed_move->end_speed = global_min_speed;
		}
		MoveListItem* next_item = last_item;
		while(next_item){
			// calculate max possible speed
			double vp1 = sqrt(next_item->feed_move->end_speed * next_item->feed_move->end_speed + global_acceleration*2*next_item->feed_move->dist);
			double vp2 = -vp1;
			if(fabs(next_item->feed_move->end_speed + vp1) < 0.00000001){
				vp2 = -vp1;
			}
			double t1 = 2*next_item->feed_move->dist / (next_item->feed_move->end_speed + vp1);
			if(fabs(next_item->feed_move->end_speed + vp2)<0.000000001){
				vp2 = -vp1;
			}
			double t2 = 2*next_item->feed_move->dist / (next_item->feed_move->end_speed + vp2);
			double vp = vp1;
			if(t1<0 || t2>=0 && t2<t1){
				vp = vp2;
			}
			if(next_item->feed_move->start_speed>vp){
				next_item->feed_move->start_speed = vp;
				if(next_item->prev_item){
					next_item->prev_item->feed_move->end_speed = vp;
				}
				next_item->feed_move->half_way = 0;// decelerating throughout
			}
			else{
				next_item->feed_move->SetHalfway(global_acceleration);
			}
			next_item = next_item->prev_item;
		}

		next_item = first_item;
		int problem_with_move = 0;
		while(next_item && !problem_with_move){
			problem_with_move = move_straight1(next_item->feed_move, global_acceleration, 0);
			next_item = next_item->next_item;
		}

		// enable the interrupts
		outp(0xA1,0x00);
		outp(0x21,0x00);
		Mouse_On(&temp_mouse, &temp_mouse, &temp_mouse);//unmask all interrupts

		next_item = first_item;
		while(next_item){
			MoveListItem* this_item = next_item;
			next_item = next_item->next_item;
			delete this_item->feed_move;
			delete this_item;
		}

		num_moves = 0;
		first_item = NULL;
		last_item = NULL;

		return problem_with_move;
	}
#endif


void main(void)
{
	FILE *fp;
	FILE *tap_fp;
	char command[512], number[80];
	char last_command[512]="";
	double value;
	int temp_mouse;
	int count = 0, bufpos = 0;
	int move_type=0; // 0 is move incremental, 1 is move absolute, 2 is rapid
	int move_type_before_tap_file;

	fp =fopen(global_specs_file_name, "rb");

	while(1)
	{
		int bufpos=0;
		read_first_word(fp, command, command, &bufpos, 1, 1);

		if(!stricmp(command, "min_speed"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%lf", &global_min_speed);
		}
		else if(!stricmp(command, "max_speed"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%lf", &global_max_speed);
		}
		else if(!stricmp(command, "acceleration"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%lf", &global_acceleration);
		}
		else if(!stricmp(command, "feed_speed"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%lf", &global_feed_speed);
		}
		else if(!stricmp(command, "x_steps_per_unit"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%lf", &global_x_steps_per_unit);
		}
		else if(!stricmp(command, "y_steps_per_unit"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%lf", &global_y_steps_per_unit);
		}
		else if(!stricmp(command, "z_steps_per_unit"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%lf", &global_z_steps_per_unit);
		}
		else if(!stricmp(command, "units"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%d", &global_units);
		}
		else if(!stricmp(command, "x_backlash_steps"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%lf", &global_x_backlash_steps);
		}
		else if(!stricmp(command, "y_backlash_steps"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%lf", &global_y_backlash_steps);
		}
		else if(!stricmp(command, "use_feed_move_buffer"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%d", &global_use_feed_move_buffer);
		}
		else if(!stricmp(command, "interrupt_21"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%d", &global_interrupt_21);
		}
		else if(!stricmp(command, "interrupt_A1"))
		{
			fscanf (fp, "%s", number);
			sscanf(number, "%d", &global_interrupt_A1);
		}
		else if(!stricmp(command, "x_plus"))
		{
			fscanf (fp, "%s", number);
			double wanted, needed;
			sscanf(number, "%lf %lf", &wanted, &needed);
			x_plus_graph.AddAValue(wanted, needed);
		}
		else if(!stricmp(command, "x_minus"))
		{
			fscanf (fp, "%s", number);
			double wanted, needed;
			sscanf(number, "%lf %lf", &wanted, &needed);
			x_minus_graph.AddAValue(wanted, needed);
		}
		else if(!stricmp(command, "y_plus"))
		{
			fscanf (fp, "%s", number);
			double wanted, needed;
			sscanf(number, "%lf %lf", &wanted, &needed);
			y_plus_graph.AddAValue(wanted, needed);
		}
		else if(!stricmp(command, "y_minus"))
		{
			fscanf (fp, "%s", number);
			double wanted, needed;
			sscanf(number, "%lf %lf", &wanted, &needed);
			y_minus_graph.AddAValue(wanted, needed);
		}
		else if(!stricmp(command, "arc_speed"))
		{
			fscanf (fp, "%lf", &global_arc_speed);
		}
		else if(!stricmp(command, "parallel_port"))
		{
			fscanf (fp, "%d", &global_parallel_port);
		}

		else if(feof(fp))break;
		count++;
		if(count>2000)break;
	}

	/* get ready for output */
	init_output();

	welcome_notice();
	help_notice();
	
	// initialize things for reading from the keyboard
	bufpos = 0;

	//load position etc.
	load_things();
	
	init_output();
	
	while(1)
	{
		int exit_found = 0;
		int doing_tap_file = 0;
		int waiting_for_continue = 0;
		int problem_with_move = 0;
		
		do
		{
			int repeat=1, repeat_count = 0;
			
			do{//while(repeat)
				//interpret line
				bufpos = 0;
				double next_x_value, next_y_value, next_z_value;
				double next_feed_value, next_i_value, next_j_value;
				double next_r_value, next_d_value;
				double next_dwell_value;
				int move_found = 0, g0_found = 0, g1_found = 0;
				int g2_found = 0, g3_found = 0;
				int g4_found = 0;
				int x_found = 0, y_found = 0, z_found = 0;
				int i_found = 0, j_found = 0;
				int feed_found = 0;
				int tap_found = 0, tr_found = 0;
				int m2_found = 0, set_found = 0;
				int wait_found = 0, d_found = 0, help_found = 0;
				int mm_found = 0, steps_found = 0;
				int tool_found = 0, tool_value = 0;
				int tool_change_done_recently = 0, m6_found = 0;
				int cont_found = 0, dwell_found = 0;
				int zerox_found = 0, zeroy_found = 0;
				char tap_file[256];

				// tell the user where we are
				if(!repeat_count){
					print_where_we_are(doing_tap_file, waiting_for_continue, move_type);
				}
				//read a line from the keyboard
				if(repeat_count)strcpy(command, last_command);
				else if(!doing_tap_file || waiting_for_continue)read_first_word(fp, command, command, &bufpos, 3, 0);
				else//doing tap file
				{
					if(kbhit()){
						cout<<"key pressed during tap file, type cont to continue\n";
						waiting_for_continue = 1;
						getch();//remove the key press from buffer
						repeat = 0;
						continue;
					}

					read_first_word(tap_fp, command, command, &bufpos, 1, 0);
					if(feof(tap_fp))
					{
						doing_tap_file = 0;
						drill_onoff(0);
						cout<<"End of tap file, please stop the spindle and turn off the coolant\n";
						fclose(tap_fp);
						move_type = move_type_before_tap_file;
						repeat = 0;
						strcpy(command, last_command);
						continue;
					}
					//cout<<command<<"\n";
				}

				bufpos = 0;

				while(1)
				{
					// keep looking for individual meanings until an end of line
					if(!command[bufpos])break;

					look_for_meaning(command, &bufpos, &move_found, &g0_found,
						&g1_found, &g2_found, &g3_found, &g4_found, &next_x_value,
						&next_y_value, &next_z_value, &next_i_value, &next_j_value,
						&exit_found, &x_found, &y_found, &z_found, &i_found, &j_found,
						&tap_found, tap_file, &tr_found,
						&m2_found, &feed_found, &next_feed_value, &set_found,
						&wait_found, &d_found, &next_d_value, &mm_found, &steps_found, &help_found,
						&tool_found, &tool_value, &m6_found, &cont_found, &dwell_found, &next_dwell_value,
						&zerox_found, &zeroy_found);
				}// end of while(1)

				// set move type
				if(move_found)move_type = 0;
				else if(g0_found)move_type = 1;
				else if(g1_found)move_type = 2;
				else if(g2_found)move_type = 3;
				else if(g3_found)move_type = 4;

#if 0
				// if feed move found then add it to the feed buffer
				if(global_use_feed_move_buffer && doing_tap_file){

					// only add move, if there is a feed move
					if((x_found || y_found || z_found) && move_type == 2){
						if(feed_found)globals.feed_value = next_feed_value;
						if(z_found && next_z_value >999){
						z_found = 1;
						}
						feed_move_buffer.AddFeedMove(x_found, next_x_value, y_found, next_y_value, z_found, next_z_value);
						repeat = 0;
						continue;
					}

					if((x_found || y_found || z_found) || exit_found || tap_found || wait_found || tr_found || m2_found || set_found || tool_found){
						feed_move_buffer.DoAllFeedMovesAndClear();
					}
				}
#endif

				if(exit_found)break;
				if(tap_found)
				{
					if(doing_tap_file && waiting_for_continue){
						fclose(tap_fp);
						doing_tap_file = 0;
					}

					if(doing_tap_file){
						cout<<"already doing tap file\n";
					}
					else
					{
						tap_fp =fopen(tap_file, "rb");
						if(tap_fp !=NULL)
						{
							doing_tap_file = 1;
							drill_onoff(1);
							cout<<tap_file<<" started\n";
							move_type_before_tap_file = move_type;
							move_type = 1;//g0 move type
							strcpy(last_command, command);
							repeat = 0;
							continue;
						}
						else cout<<"sorry could not find: "<<tap_file<<"\n";
					}
				}

				if(wait_found){
					cout<<"****WAITING FOR KEY PRESS****";
					getch();
					cout<<"\n";
					repeat = 0;
					continue;
				}

				if(tool_found == 1){
					if(m6_found){
						z_found = 0; // ignore any z move
					}
					else{
						tool_found = 0;
					}
				}

				if(help_found){
					help_notice();
				}

				if(mm_found)global_units = 1;
				if(steps_found)global_units = 0;

				if(d_found)drill_onoff(next_d_value != 0);
				
				if(feed_found)globals.feed_value = next_feed_value;
				
				if((m2_found || problem_with_move) && doing_tap_file)
				{
					if(m2_found && !problem_with_move){
						// rapid up a long way ( switches will stop motion )
						spindle_off();
						if(get_z_switches_type()){
							int move_result = move_straight(0, 0, 1000000, global_min_speed, global_max_speed, global_acceleration, 0);
							if(move_result != 2){// not top reached
								problem_with_move = 1;
							}
						}
						spindle_off_cancel();
					}
					doing_tap_file = 0;
					drill_onoff(0);
					if(problem_with_move){
						problem_with_move = 0;
						cout<<"tap file stopped\n";
					}
					else
						cout<<"m2 found, tap file ended\n";
					fclose(tap_fp);
					move_type = move_type_before_tap_file;
					repeat = 0;
					strcpy(command, last_command);
					continue;
				}

				if(set_found){
					if(x_found)globals.x_offset = next_x_value - GetXWanted(globals.x_value);
					if(y_found)globals.y_offset = next_y_value - GetYWanted(globals.y_value);
					if(z_found)globals.tools[globals.current_tool] -= (globals.z_value - next_z_value);
					repeat = 0;
					continue;
				}

				if(zerox_found){
					//move a long way x minus
					problem_with_move = move_straight(-10000000, 0, 0, global_min_speed, global_max_speed, global_acceleration, 0);
					if(problem_with_move != 3)continue;
					// limit hit, rapid x about 3 mm
					problem_with_move = move_straight(1500, 0, 0, global_min_speed, global_max_speed, global_acceleration, 1);
					if(problem_with_move)continue;
					// feed x minus slowly a long way
					double max_speed = global_feed_speed * 0.1;
					if(max_speed > global_max_speed)max_speed = global_max_speed;
					problem_with_move = move_straight(-10000000, 0, 0, global_min_speed, max_speed, global_acceleration, 0);
					if(problem_with_move != 3)continue;
					// set this as x0
					globals.x_value = 0;

					// get off the x limit
					problem_with_move = move_straight(1500, 0, 0, global_min_speed, global_max_speed, global_acceleration, 1);
					if(problem_with_move)continue;
				}

				if(zeroy_found){
					//move a long way y minus
					problem_with_move = move_straight(0, -10000000, 0, global_min_speed, global_max_speed, global_acceleration, 0);
					if(problem_with_move != 3)continue;
					// limit hit, rapid y about 3 mm
					problem_with_move = move_straight(0, 1500, 0, global_min_speed, global_max_speed, global_acceleration, 1);
					if(problem_with_move)continue;
					// feed y minus slowly a long way
					double max_speed = global_feed_speed * 0.1;
					if(max_speed > global_max_speed)max_speed = global_max_speed;
					problem_with_move = move_straight(0, -10000000, 0, global_min_speed, max_speed, global_acceleration, 0);
					if(problem_with_move != 3)continue;
					// set this as y0
					globals.y_value = 0;

					// get off the y limit
					problem_with_move = move_straight(0, 1500, 0, global_min_speed, global_max_speed, global_acceleration, 1);
					if(problem_with_move)continue;
				}

				if(move_type == 0)
				{
					if(!x_found)next_x_value = 0;
					next_x_value += GetXWanted(globals.x_value);
					if(!y_found)next_y_value = 0;
					next_y_value += GetYWanted(globals.y_value);
					if(!z_found)next_z_value = -globals.tools[globals.current_tool];
					next_z_value += (globals.z_value + globals.tools[globals.current_tool]);
				}
				else{
					if(!x_found)next_x_value = GetXWanted(globals.x_value);
					else next_x_value -= globals.x_offset;
					if(!y_found)next_y_value = GetYWanted(globals.y_value);
					else next_y_value -= globals.y_offset;
					if(!z_found)next_z_value = globals.z_value;
				}

				if(tool_found == 1 || tr_found){// t2 ( for example ) or tr ( reset all tools z values )
					spindle_off();
					// rapid up a long way ( switches will stop motion )
					if(get_z_switches_type()){
						int move_result = move_straight(0, 0, 1000000, global_min_speed, global_max_speed, global_acceleration, 0);
						if(tool_found == 1 && (tool_value<1 || tool_value>NUM_TOOLS)){
							cout<<"TOOL "<<tool_value<<" is not valid";
							problem_with_move = 1;
							repeat = 0;
							continue;
						}
						if(move_result != 2){// not top reached
							problem_with_move = 1; // stop tap file etc.
							repeat = 0;
							continue;
						}
					}
					spindle_off_cancel();
					tool_change_done_recently = 1;
					next_z_value = globals.z_value;
				}

				if(cont_found)waiting_for_continue = 0;

				// do a move
				if(x_found || y_found || z_found)
				{
					int backlash_x_found = 0, backlash_y_found =0;
					double old_wanted_x = GetXWanted(globals.x_value);
					double old_wanted_y = GetYWanted(globals.y_value);

					if(x_found && !(move_type == 3 || move_type == 4)){
						int old_xdir = globals.xdir;
						if(next_x_value - old_wanted_x >= 0)globals.xdir = 1;
						else globals.xdir = -1;
						if(globals.xdir == -old_xdir)backlash_x_found = 1;
					}
					if(y_found && !(move_type == 3 || move_type == 4)){
						int old_ydir = globals.ydir;
						if(next_y_value - old_wanted_y >= 0)globals.ydir = 1;
						else globals.ydir = -1;
						if(globals.ydir == -old_ydir)backlash_y_found = 1;
					}

					if(backlash_x_found){
						double backlash_x = GetXNeeded(old_wanted_x) - globals.x_value;
						move_straight(backlash_x, 0, 0, 0, global_max_speed, global_acceleration, 1);
					}
					if(backlash_y_found){
						double backlash_y = GetXNeeded(old_wanted_y) - globals.y_value;
						move_straight(0, backlash_y, 0, 0, global_max_speed, global_acceleration, 1);
					}

					double x_to_move = GetXNeeded(next_x_value) - globals.x_value;
					double y_to_move = GetYNeeded(next_y_value) - globals.y_value;
					double z_to_move = next_z_value - globals.z_value;

					if(move_type == 0 || move_type == 1){// move(relative) or g0(absolute) mode
						if(fabs(z_to_move)>0.0001){
							if(z_to_move>0){
								problem_with_move = move_straight(0, 0, z_to_move, global_min_speed, global_max_speed, global_acceleration, 0);
								if(!problem_with_move)problem_with_move = move_straight(x_to_move, y_to_move, 0, global_min_speed, global_max_speed, global_acceleration, 0);
							}
							else{
								problem_with_move = move_straight(x_to_move, y_to_move, 0, global_min_speed, global_max_speed, global_acceleration, 0);
								problem_with_move = move_straight(0, 0, z_to_move, global_min_speed, global_max_speed, global_acceleration, 0);
							}
						}
						else{
							problem_with_move = move_straight(x_to_move, y_to_move, z_to_move, global_min_speed, global_max_speed, global_acceleration, 0);
						}
					}
					else if(move_type == 2){//g1 (feed) mode
						double max_speed = global_feed_speed * (globals.feed_value/100);
						if(max_speed > global_max_speed)max_speed = global_max_speed;
						problem_with_move = move_straight(x_to_move, y_to_move, z_to_move, global_min_speed, max_speed, global_acceleration, 0);
					}
					else if(move_type == 3 || move_type == 4){// either g2 or g3 arc modes
						double max_speed = global_feed_speed * (globals.feed_value/100);
						if(max_speed > global_max_speed)max_speed = global_max_speed;
						globals.i_value = next_i_value;
						globals.j_value = next_j_value;
						problem_with_move = move_arc(next_x_value, next_y_value, global_arc_speed * global_min_speed, global_arc_speed * max_speed, global_arc_speed * global_acceleration, 1 - 2*(move_type == 3), 0);
						if(!problem_with_move && z_found)problem_with_move = move_straight(0, 0, next_z_value - globals.z_value, global_min_speed, max_speed, global_acceleration, 0);
						
					}

					if(problem_with_move == 3){
						// limit hit
						cout<<"!!!!! LIMIT HIT !!!!! turn off 30V, manually move off limits, then turn on 30V\n";
						cout<<"YOUR CO-ORDINATES WILL NOW BE WRONG ON ANY AXES THAT WERE MOVING\n";
					}

				}

				if(dwell_found){
					globals.dwell_value = next_dwell_value;
				}

				if(g4_found){
					// wait dwell_value seconds
					wait_for_seconds(globals.dwell_value);
				}

				if(tr_found){
					for(int i = 0; i<NUM_TOOLS; i++){
						globals.tools[i] = 0;
						globals.z_value = 0;
					}
				}
				else if(tool_change_done_recently){
					cout<<"Please Insert TOOL "<<tool_value<<", Start Spindle, then press SPACE key to continue\n";
					int c = getch();
					while(c != ' '){
						c = getch();
					}
					globals.current_tool = tool_value - 1;
				}
				else if(tool_found == 2){// tc2 (for example , meaning change tool length offset, but don't return to the top )
					if(tool_value<1 || tool_value>NUM_TOOLS){
						cout<<"TOOL "<<tool_value<<" is not valid";
						problem_with_move = 1;
						repeat = 0;
						continue;
					}
					globals.current_tool = tool_value - 1;
				}

				if(!doing_tap_file && strlen(command) == 1 && (command[0] == 'p' || command[0] == 'P'))
				{
					cout<<"Repeating Last Command:"<<last_command<<"\n";
					if(repeat_count)repeat=0;
					repeat_count++;
				}
				else repeat = 0;
			}while(repeat);

			if(!doing_tap_file)strcpy(last_command, command);

		}while(!exit_found);

		if(exit_found)break;

	}// end of while(1)

	save_things();

	goodbye_notice();
}//end of main()

